from __future__ import annotations

"""
领域模型与枚举定义
------------------
职责：
- 定义数据库表（SQLModel）与业务枚举
- 提供 UTC 时间生成函数 `utcnow`

说明：
- 表名通过 `__tablename__` 显式指定，便于数据库可读性与迁移
- 金额/数量类型使用 Decimal + Numeric 保证精度
"""

import datetime as dt
from zoneinfo import ZoneInfo
from decimal import Decimal
from enum import Enum
from typing import Any, Dict, List, Optional

from sqlalchemy import (
    Boolean, Column, Date, DateTime, ForeignKey, Index, Integer, 
    Numeric, String, Text, UniqueConstraint
)
from sqlalchemy.sql import expression
from sqlalchemy.dialects.postgresql import JSONB
from sqlmodel import Field, SQLModel

BEIJING_TZ = ZoneInfo("Asia/Shanghai")


def utcnow() -> dt.datetime:
    """返回当前 UTC 时间（带时区）。"""
    return dt.datetime.now(dt.timezone.utc).replace(microsecond=0)


class RoleEnum(str, Enum):
    """系统角色：管理员/普通用户。"""
    admin = "admin"
    user = "user"


class ScriptStatus(str, Enum):
    """策略状态：启用/暂停。"""
    active = "active"
    paused = "paused"


class MarketType(str, Enum):
    """市场类型：现货/永续。"""
    spot = "spot"
    perpetual = "perpetual"


class SignalStatus(str, Enum):
    """信号处理状态。"""
    accepted = "accepted"
    processing = "processing"
    partial = "partial"
    filled = "filled"
    error = "error"


class OrderStatus(str, Enum):
    """订单执行状态。"""
    sent = "sent"
    filled = "filled"
    partial = "partial"
    rejected = "rejected"
    error = "error"


class TradeSide(str, Enum):
    """交易方向：买/卖。"""
    buy = "BUY"
    sell = "SELL"


class OrderType(str, Enum):
    """下单类型：市价/限价。"""
    market = "MARKET"
    limit = "LIMIT"


class PositionAction(str, Enum):
    """仓位动作：开仓/平仓。"""
    open = "OPEN"
    close = "CLOSE"


class User(SQLModel, table=True):
    """用户表。

    - `role` 控制权限（管理员/用户）
    - `global_pause` 用户一键全局暂停订阅
    - `tv_enabled` 是否允许接收 TradingView 信号
    """
    __tablename__ = "users"

    id: Optional[int] = Field(default=None, primary_key=True)
    email: str = Field(
        sa_column=Column(String, unique=True, nullable=False, index=True)
    )
    password_hash: str = Field(nullable=False)
    role: RoleEnum = Field(default=RoleEnum.user, nullable=False)
    tv_enabled: bool = Field(default=True, nullable=False)
    global_pause: bool = Field(default=False, nullable=False)
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))


class Script(SQLModel, table=True):
    """策略表。

    - 仅保留 `name`、`symbol`、`market_type` 三要素
    - `webhook_id` 用于拼接 webhook URL
    - `url_token_hash`/`body_secret_hash` 存储哈希；明文仅在创建/旋转时返回
    - `status` 控制策略启停
    """
    __tablename__ = "scripts"

    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(nullable=False)
    symbol: str = Field(nullable=False)
    market_type: MarketType = Field(sa_column=Column(String, nullable=False))
    webhook_id: str = Field(
        sa_column=Column(String, unique=True, nullable=False, index=True)
    )
    url_token_hash: str = Field(nullable=False)
    body_secret_hash: str = Field(nullable=False)
    status: ScriptStatus = Field(default=ScriptStatus.active, nullable=False)
    hidden: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=expression.false()),
    )
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))


class ScriptSubscription(SQLModel, table=True):
    """策略订阅表（用户维度）。

    - `enabled` 表示是否对该策略生效
    - `allocation` 资金分配（JSON），与前端配置联动
    - `max_notional`、`slippage_bps` 风控相关，前端可不传则走默认
    - `account_name`、`connector_name` 对应 hummingbot 账户与连接器
    """
    __tablename__ = "script_subscriptions"
    __table_args__ = (
        UniqueConstraint("user_id", "script_id", name="uq_user_script"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    user_id: int = Field(foreign_key="users.id", nullable=False)
    script_id: int = Field(foreign_key="scripts.id", nullable=False)
    enabled: bool = Field(default=True, nullable=False)
    allocation: Dict[str, Any] = Field(sa_column=Column(JSONB, nullable=False))
    max_notional: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(24, 8), nullable=True),
    )
    slippage_bps: Optional[int] = Field(default=None, nullable=True)
    account_name: Optional[str] = Field(default=None, nullable=True)
    connector_name: Optional[str] = Field(default=None, nullable=True)
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))
    hidden: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=expression.false()),
    )


class ScriptSignal(SQLModel, table=True):
    """Webhook 接收的原始信号记录。用于幂等与追踪。"""
    __tablename__ = "script_signals"
    __table_args__ = (
        UniqueConstraint("script_id", "nonce", name="uq_script_nonce"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    script_id: Optional[int] = Field(
        default=None,
        sa_column=Column(Integer, ForeignKey("scripts.id", ondelete="SET NULL"), nullable=True),
    )
    nonce: str = Field(nullable=False)
    payload_json: Dict[str, Any] = Field(sa_column=Column(JSONB, nullable=False))
    status: SignalStatus = Field(default=SignalStatus.accepted, nullable=False)
    error: Optional[str] = Field(default=None)
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))
    processed_at: Optional[dt.datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )


class ExecOrder(SQLModel, table=True):
    """执行订单记录。与外部交易执行系统对接的结果追踪。"""
    __tablename__ = "exec_orders"

    id: Optional[int] = Field(default=None, primary_key=True)
    signal_id: Optional[int] = Field(foreign_key="script_signals.id", nullable=True)
    user_id: Optional[int] = Field(foreign_key="users.id", nullable=True)
    account_name: str = Field(nullable=False)
    connector: str = Field(nullable=False)
    symbol: str = Field(nullable=False)
    side: TradeSide = Field(nullable=False)
    order_type: OrderType = Field(nullable=False)
    position_action: PositionAction = Field(nullable=False)
    qty: Decimal = Field(sa_column=Column(Numeric(24, 10), nullable=False))
    price: Optional[Decimal] = Field(sa_column=Column(Numeric(24, 10), nullable=True))
    status: OrderStatus = Field(default=OrderStatus.sent, nullable=False)
    error: Optional[str] = Field(default=None)
    tx_ref: Optional[str] = Field(default=None)
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))


class Group(SQLModel, table=True):
    """账户分组。用于对多账户进行编排/加权。"""
    __tablename__ = "groups"

    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(nullable=False, unique=True)
    connector_name: str = Field(nullable=False)
    desc: Optional[str] = Field(default=None, sa_column=Column(String, nullable=True))


class GroupMember(SQLModel, table=True):
    """分组成员。记录账户加入某分组时的权重与启用状态。"""
    __tablename__ = "group_members"
    __table_args__ = (
        UniqueConstraint("group_id", "account_name", name="uq_group_account"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    group_id: int = Field(foreign_key="groups.id", nullable=False)
    account_name: str = Field(nullable=False)
    connector_name: str = Field(nullable=False)
    weight: int = Field(default=1, nullable=False)
    enabled: bool = Field(default=True, nullable=False)


class AuditLog(SQLModel, table=True):
    """审计日志。记录关键操作与请求载荷，便于排查问题。"""
    __tablename__ = "audit_logs"

    id: Optional[int] = Field(default=None, primary_key=True)
    actor_id: Optional[int] = Field(foreign_key="users.id", nullable=True)
    action: str = Field(nullable=False)
    target: str = Field(nullable=False)
    payload_json: Dict[str, Any] = Field(sa_column=Column(JSONB, nullable=False))
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))


# ========== C 阶段：数据库与记录 ==========

class RunMode(str, Enum):
    """运行模式：影子/模拟/实盘"""
    shadow = "shadow"
    paper = "paper"
    live = "live"


class DecisionStatus(str, Enum):
    """决策状态"""
    DRYRUN = "DRYRUN"
    APPROVED = "APPROVED"
    EXECUTING = "EXECUTING"  # 执行中（防重复下单）
    EXECUTED = "EXECUTED"
    REJECTED = "REJECTED"
    BAD_ARGS = "BAD_ARGS"
    ERROR = "ERROR"


class Run(SQLModel, table=True):
    """运行批次表"""
    __tablename__ = "runs"
    __table_args__ = (
        Index("idx_runs_started_at", "started_at"),
    )

    run_id: str = Field(sa_column=Column(Text, primary_key=True, nullable=False))
    model: str = Field(sa_column=Column(Text, nullable=False))
    mode: RunMode = Field(sa_column=Column(String, nullable=False))
    started_at: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )
    notes: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    # 调度器配置（用于重启后恢复）
    exchange: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    symbol: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    account_name: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    risk_config: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True),
        description="风控配置（用于重启后恢复调度器）"
    )
    interval_seconds: Optional[int] = Field(default=None, nullable=True)


class Snapshot(SQLModel, table=True):
    """决策前快照表"""
    __tablename__ = "snapshots"
    __table_args__ = (
        Index("idx_snapshots_run_ts", "run_id", "ts"),
        Index("idx_snapshots_exchange_symbol_ts", "exchange", "symbol", "ts"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    run_id: str = Field(sa_column=Column(Text, nullable=False))
    ts: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )
    exchange: str = Field(nullable=False)
    symbol: str = Field(nullable=False)
    account_name: str = Field(nullable=False)
    payload: Dict[str, Any] = Field(
        sa_column=Column(JSONB, nullable=False),
        description="快照数据：包含 ticker, funding, positions, balance"
    )


class Decision(SQLModel, table=True):
    """模型动作与后端判定表"""
    __tablename__ = "decisions"
    __table_args__ = (
        Index("idx_decisions_run_ts", "run_id", "ts"),
        Index("idx_decisions_status_ts", "status", "ts"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    run_id: str = Field(sa_column=Column(Text, nullable=False))
    ts: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )
    action_json: Dict[str, Any] = Field(
        sa_column=Column(JSONB, nullable=False),
        description="动作 JSON，来自风控规划器"
    )
    status: DecisionStatus = Field(
        default=DecisionStatus.DRYRUN,
        sa_column=Column(String, nullable=False)
    )
    result: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True),
        description="执行结果或拟执行参数"
    )
    reason: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))


class AgentStrategy(SQLModel, table=True):
    """Agent 策略配置（统一调度器使用）"""
    __tablename__ = "agent_strategies"

    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(nullable=False, unique=True, description="策略名称")
    desc: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True), description="策略描述")
    account_name: str = Field(nullable=False, description="账户名称")
    connector_name: str = Field(nullable=False, description="交易所/连接器名称")
    trading_pair: str = Field(nullable=False, description="交易对，如 BTC-USDT")
    mode: RunMode = Field(default=RunMode.shadow, sa_column=Column(String, nullable=False))
    interval_seconds: int = Field(default=120, nullable=False, description="执行间隔秒数")
    max_notional_usd: float = Field(default=0, nullable=False, description="最大名义金额（USD）")
    max_slippage_bps: int = Field(default=0, nullable=False, description="最大滑点（bps）")
    max_daily_loss_usd: float = Field(default=0, nullable=False, description="最大日亏损（USD）")
    enabled: bool = Field(default=True, nullable=False, description="是否启用")
    last_heartbeat: Optional[dt.datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
        description="Agent 最近一次心跳时间（AgentManager 更新）",
    )
    created_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))
    updated_at: dt.datetime = Field(default_factory=utcnow, sa_column=Column(DateTime(timezone=True), nullable=False))


class AIAutoDecision(SQLModel, table=True):
    """AI 自动交易决策流水"""
    __tablename__ = "ai_auto_decisions"
    __table_args__ = (
        Index("idx_ai_decisions_run_ts", "run_id", "ts"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    run_id: str = Field(sa_column=Column(Text, nullable=False))
    ts: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )
    account_name: str = Field(sa_column=Column(Text, nullable=False))
    connector_name: str = Field(sa_column=Column(Text, nullable=False))
    trading_pair: str = Field(sa_column=Column(Text, nullable=False))
    mode: RunMode = Field(sa_column=Column(String, nullable=False))
    prompt: str = Field(sa_column=Column(Text, nullable=False))
    request_params: Dict[str, Any] = Field(sa_column=Column(JSONB, nullable=False))
    response_text: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    finish_reason: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    tool_invocations: Optional[List[Dict[str, Any]]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True)
    )
    tokens_used: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True)
    )
    candles: Optional[List[Dict[str, Any]]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True),
        description="本次决策使用的 K 线数据（已截断）"
    )


class RSSFeedItem(SQLModel, table=True):
    """RSSHub 抓取的资讯条目"""
    __tablename__ = "rss_feed_items"
    __table_args__ = (
        UniqueConstraint("source", "guid", name="uq_rss_source_guid"),
        Index("idx_rss_delivered_source", "delivered", "source"),
        Index("idx_rss_published_at", "published_at"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    source: str = Field(sa_column=Column(String, nullable=False, index=True))
    guid: str = Field(sa_column=Column(String, nullable=False))
    title: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    link: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    author: Optional[str] = Field(default=None, sa_column=Column(String, nullable=True))
    summary: Optional[str] = Field(default=None, sa_column=Column(Text, nullable=True))
    published_at: Optional[dt.datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    fetched_at: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False),
    )
    delivered: bool = Field(
        default=False,
        sa_column=Column(Boolean, nullable=False, server_default=expression.false()),
    )
    delivered_at: Optional[dt.datetime] = Field(
        default=None,
        sa_column=Column(DateTime(timezone=True), nullable=True),
    )
    raw: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True),
        description="原始 RSSHub 响应",
    )


class TradingOrder(SQLModel, table=True):
    """订单表"""
    __tablename__ = "orders"
    __table_args__ = (
        Index("idx_orders_exchange_symbol_created", "exchange", "symbol", "created_at"),
        Index("idx_orders_client_order_id", "client_order_id"),
    )

    order_id: str = Field(sa_column=Column(Text, primary_key=True, nullable=False))
    client_order_id: str = Field(
        sa_column=Column(Text, unique=True, nullable=False),
        description="格式：{run_id}-{yyyyMMddHHmmss}-{hash8}"
    )
    run_id: str = Field(sa_column=Column(Text, nullable=False))
    exchange: str = Field(nullable=False)
    symbol: str = Field(nullable=False)
    side: str = Field(nullable=False, description="buy|sell")
    order_type: str = Field(nullable=False, description="market|limit")
    price: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(24, 10), nullable=True)
    )
    amount: Decimal = Field(sa_column=Column(Numeric(24, 10), nullable=False))
    status: str = Field(nullable=False, description="订单状态")
    raw: Optional[Dict[str, Any]] = Field(
        default=None,
        sa_column=Column(JSONB, nullable=True),
        description="原始订单数据"
    )
    created_at: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )


class Fill(SQLModel, table=True):
    """成交表"""
    __tablename__ = "fills"
    __table_args__ = (
        Index("idx_fills_order_ts", "order_id", "ts"),
    )

    id: Optional[int] = Field(default=None, primary_key=True)
    order_id: str = Field(sa_column=Column(Text, nullable=False))
    price: Decimal = Field(sa_column=Column(Numeric(24, 10), nullable=False))
    qty: Decimal = Field(sa_column=Column(Numeric(24, 10), nullable=False))
    fee: Optional[Decimal] = Field(
        default=None,
        sa_column=Column(Numeric(24, 10), nullable=True)
    )
    fee_asset: Optional[str] = Field(default=None, nullable=True)
    ts: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )


class PnLDaily(SQLModel, table=True):
    """日度 PnL 表"""
    __tablename__ = "pnl_daily"
    __table_args__ = (
        UniqueConstraint("day", "run_id", name="uq_pnl_daily_day_run"),
    )

    day: dt.date = Field(sa_column=Column(Date, primary_key=True, nullable=False))
    run_id: str = Field(sa_column=Column(Text, primary_key=True, nullable=False))
    realized: Decimal = Field(
        default=Decimal("0"),
        sa_column=Column(Numeric(24, 8), nullable=False)
    )
    unrealized: Decimal = Field(
        default=Decimal("0"),
        sa_column=Column(Numeric(24, 8), nullable=False)
    )
    updated_at: dt.datetime = Field(
        default_factory=utcnow,
        sa_column=Column(DateTime(timezone=True), nullable=False)
    )
